import { nextTick, ref } from 'vue';
import { NotifyPlugin } from 'tdesign-vue-next';
import dayjs from 'dayjs';

/**
 * 检测文件后缀名是否符合要求
 * @param file  文件对象
 * @returns
 */
const isFileTypeIllegal = (file: File) => {
    const illegalFileExName = '.exe,.dll';
    const type = file.name.substring(file.name.lastIndexOf('.'));
    return illegalFileExName.indexOf(type.toLowerCase()) == -1;
};

export default function useBatchUpload({ callback }) {
    const fileList = ref([]);
    const entriesArr = ref([]);

    /**
     * 把扫描过后的文件夹中的文件展开
     */
    const getExpandFiles = (fileList) => {
        let cacheFileList = [];
        fileList.forEach((file) => {
            if (file.children) {
                cacheFileList = cacheFileList.concat(getExpandFiles(file.children));
            } else {
                cacheFileList.push(file);
            }
        });
        return cacheFileList;
    };

    /**
     *
     * @param file 单个文件或文件列表
     * @param isMulti 如果为文件列表需要把参数设置为true
     * @returns
     */
    const checkFileType = (file, isMulti = false, checkFolder = true) => {
        let isFolder = false;
        let hasErrorType = false;
        if (!isMulti) {
            hasErrorType = !isFileTypeIllegal(file);
            isFolder = !file.type;
        } else {
            hasErrorType = file.some((f) => !isFileTypeIllegal(f));
            isFolder = file.some((f) => !f.type);
        }
        if (isFolder && checkFolder) {
            NotifyPlugin.warning({
              title:'暂不支持上传文件夹'
            });
            return false;
        }
        if (hasErrorType) {
            NotifyPlugin.warning({
              title:'上传类型有误'
            });
            return false;
        }
        return true;
    };

    /**
     * 检查上传文件尺寸是否符合要求
     * @param {Array} list 文件列表
     * @param {Number} size 最大尺寸（单位：M）
     */
    const checkFileSizeInRange = (list = [], size = 30) => {
        if (!list.length) {
            return true;
        }
        const maxSize = size * 1024 * 1024;
        const totalSize = list.reduce((acc, cur) => acc + Number(cur.size), 0);
        const inRange = totalSize <= maxSize;
        if (!inRange) {
            NotifyPlugin.warning({
              title:'上传的文件不能大于30M'
            });
        }
        return inRange;
    };

    const formatFileData = (curFile, parent) => ({
        raw: curFile,
        type: curFile.type,
        name: curFile.name,
        status: 'success',
        size: curFile.size,
        updateTime: dayjs(curFile.lastModified).format('YYYY-MM-DD HH:mm'),
        trigger: 'drop',
        parent,
    });

    // 还原文件的原始位置，扫描出来的文件放到对应文件夹下
    const getFileTreeData = (list) => {
        const parentEnum = {};
        list.forEach((item) => {
            const { parent, ...restItem } = item;
            const curParentKey = parent.fullPath.split('/')[1];
            if (parentEnum[curParentKey]) {
                const curChildren = parentEnum[curParentKey].children || [];
                parentEnum[curParentKey].children = [...curChildren, restItem];
            } else {
                parent.children = [restItem];
                Object.defineProperty(parent, 'name', { value: curParentKey });
                parentEnum[curParentKey] = parent;
            }
        });
        return Object.keys(parentEnum).map((key) => {
            // 计算下文件夹下所有文件的大小
            const curChildren = parentEnum[key].children;
            const curFileSize = curChildren.reduce((acc, cur) => acc + cur.size, 0);
            parentEnum[key].size = curFileSize;
            return parentEnum[key];
        });
    };

    // 扫描出来的文件要转换成正确的raw格式File文件
    const transformToFile = () => {
        const cacheFileList = [];
        const allEntries = [];
        entriesArr.value.forEach((item) => {
            allEntries.push(...item);
        });
        if (allEntries.length) {
            allEntries.forEach(({ entry, parent }, index) => {
                const isLast = index === allEntries.length - 1;
                entry.file(
                    async (file: any) => {
                        const path = entry.fullPath.substring(1);
                        /** 修改webkitRelativePath 是核心操作，原因是拖拽会的事件体中webkitRelativePath是空的，而且webkitRelativePath 是只读属性，普通赋值是不行的。所以目前只能使用这种方法将entry.fullPath 赋值给webkitRelativePath* */
                        const newFile = Object.defineProperty(file, 'webkitRelativePath', {
                            value: path,
                        });
                        const isHideFile = !newFile.name.split('.')[0];
                        if (!isHideFile) {
                            // 过滤隐藏文件
                            cacheFileList.push(formatFileData(newFile, parent));
                        }
                        if (isLast) {
                            fileList.value.push(...getFileTreeData(cacheFileList));
                            nextTick(() => {
                                callback(fileList.value);
                                entriesArr.value = [];
                            });
                        }
                    },
                    () => {
                        if (isLast) {
                            callback(fileList.value);
                            entriesArr.value = [];
                        }
                    },
                );
            });
        } else {
            callback(fileList.value, true);
        }
    };

    // 把列表中的DirectoryEntry项替换为扫描过后的FileEntry（只有文件夹中嵌套文件夹才会用到）
    const replaceFileData = ({ initRes, nestedIndexArr, newRes }) => {
        let diffNum = 0;
        let cacheInitRes = [...initRes];
        nestedIndexArr.forEach((nestedIndex, index) => {
            const nestedIndexRes = newRes[index];
            if (nestedIndexRes.status === 'fulfilled') {
                const cacheIndex = nestedIndex + diffNum;
                diffNum += nestedIndexRes.value.length - 1;
                const startArr = cacheInitRes.slice(0, cacheIndex);
                const endArr = cacheInitRes.slice(cacheIndex + 1, cacheInitRes.length);
                cacheInitRes = [...startArr, ...nestedIndexRes.value, ...endArr];
            }
        });
        return cacheInitRes;
    };

    // 获取文件夹下文件的entries
    const readEntriesAsync = (reader, parent) =>
        new Promise((resolve) => {
            reader.readEntries((results) => {
                const cacheRes = results.map((item) => ({
                    parent,
                    entry: item,
                }));
                resolve(cacheRes);
            });
        });

    /**
     *  获取拖拽的所有文件和文件夹的entries
     * @param {Boolean} isDeep // 是否为文件夹内的文件夹
     * @param {Number} folderIndex // 为了保证处理过后的文件顺序和拖入时一致
     * */
    const getEntries = async (item, isDeep, folderIndex) =>
        new Promise((resolve) => {
            const fileEntry = isDeep ? item : item.webkitGetAsEntry();
            const isHideFile = !fileEntry.name.split('.')[0];
            if (fileEntry.isFile && !isHideFile) {
                // 文件类型，直接加入文件列表
                const curFile = item.getAsFile();
                fileList.value.push(formatFileData(curFile, {}));
                resolve(1);
            } else if (fileEntry.isDirectory) {
                // 文件夹，要扫描后获取全部文件
                const directoryReader = fileEntry.createReader();
                readEntriesAsync(directoryReader, fileEntry).then((res: any[]) => {
                    // 处理文件夹内嵌套文件夹
                    const promiseArr = [];
                    const nestedIndexArr = []; // 文件夹内嵌套的文件夹的索引，用于后面扫描出文件进行替换
                    res.forEach(({ entry }, index) => {
                        if (!entry.isFile) {
                            nestedIndexArr.push(index);
                            promiseArr.push(getEntries(entry, true, folderIndex));
                        }
                    });
                    if (promiseArr.length) {
                        Promise.allSettled(promiseArr).then((newRes) => {
                            const cacheRes = replaceFileData({
                                initRes: res,
                                nestedIndexArr,
                                newRes,
                            });
                            entriesArr.value[folderIndex] = cacheRes;
                            resolve(cacheRes);
                        });
                    } else {
                        entriesArr.value[folderIndex] = res;
                        resolve(res);
                    }
                });
            } else {
                resolve([]);
            }
        });

    const scanFiles = async (items) => {
        const promiseArr = [];
        if (items.length) {
            entriesArr.value = new Array(items.length);
        }
        for (let i = 0; i < items.length; i++) {
            const item = items[i];
            promiseArr.push(getEntries(item, false, i));
        }
        Promise.allSettled(promiseArr).then((res) => {
            transformToFile();
        });
    };

    const removeFile = (index) => {
        fileList.value.splice(index, 1);
    };

    const addFiles = (files) => {
        fileList.value = fileList.value.concat(files);
    };

    const resetFileList = () => {
        fileList.value = [];
    };

    const replaceFileList = (files) => {
        fileList.value = files;
    };

    return {
        fileList,
        scanFiles,
        checkFileSizeInRange,
        checkFileType,
        addFiles,
        removeFile,
        resetFileList,
        replaceFileList,
        getExpandFiles,
    };
}
